using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Reflection;

using NUnit.Framework;
using VistaDB.Provider;

using NSimpleDB.Service.Contract;

namespace test.Service.VistaDb.Blackbox
{
    [TestFixture]
    public class test_Domain_Operations
    {
        [Test]
        public void testOpenClose()
        {
            System.IO.File.Delete("temp.vdb3");
            using (ISimpleDBService s = new NSimpleDB.Service.VistaDb.VistaDbSimpleDBService())
            {
                s.Open("temp.vdb3");
            }
            Assert.IsTrue(System.IO.File.Exists("temp.vdb3"));

            DataTable tb = DBHelpers.GetDomainDirectory("temp.vdb3", true);
            Assert.AreEqual(3, tb.Columns.Count);
            Assert.AreEqual("idDomain", tb.Columns[0].ColumnName);
            Assert.AreEqual("domainName", tb.Columns[1].ColumnName);
            Assert.AreEqual("scheduledForDeletion", tb.Columns[2].ColumnName);
        }


        [Test]
        public void testCreateDomain()
        {
            System.IO.File.Delete("temp.vdb3");
            using (ISimpleDBService s = new NSimpleDB.Service.VistaDb.VistaDbSimpleDBService("temp.vdb3"))
            {
                s.CreateDomain("d1");
            }

            DataTable tb = DBHelpers.GetDomainDirectory("temp.vdb3", false);
            Assert.AreEqual(1, tb.Rows.Count);
            Assert.AreEqual("d1", tb.Rows[0]["domainName"]);


            DataTable tbD = DBHelpers.GetDomainByIdDomain("temp.vdb3", (int)tb.Rows[0]["idDomain"]);
            Assert.AreEqual(3, tbD.Columns.Count);
            Assert.AreEqual("itemName", tbD.Columns[0].ColumnName);
            Assert.AreEqual("attributeName", tbD.Columns[1].ColumnName);
            Assert.AreEqual("attributeValue", tbD.Columns[2].ColumnName);


            // reopen db. the prev domain should still be there
            using (ISimpleDBService s = new NSimpleDB.Service.VistaDb.VistaDbSimpleDBService("temp.vdb3"))
            {
                s.CreateDomain("d2");
            }

            tb = DBHelpers.GetDomainDirectory("temp.vdb3", false);
            Assert.AreEqual(2, tb.Rows.Count);
            Assert.AreEqual("d2", tb.Rows[1]["domainName"]);

            tbD = DBHelpers.GetDomainByIdDomain("temp.vdb3", (int)tb.Rows[1]["idDomain"]);
            Assert.AreEqual(3, tbD.Columns.Count);


            // create another domain with the same name as a previous one
            // this should not (!) throw an exception
            using (ISimpleDBService s = new NSimpleDB.Service.VistaDb.VistaDbSimpleDBService("temp.vdb3"))
            {
                s.CreateDomain("d1");
            }

            tb = DBHelpers.GetDomainDirectory("temp.vdb3", false);
            Assert.AreEqual(2, tb.Rows.Count);
            Assert.AreEqual("d1", tb.Rows[0]["domainName"]);
            Assert.AreEqual("d2", tb.Rows[1]["domainName"]);
        }


        [Test]
        public void testDeleteDomain()
        {
            DataTable tb;
            int idDomain;

            System.IO.File.Delete("temp.vdb3");
            using (ISimpleDBService s = new NSimpleDB.Service.VistaDb.VistaDbSimpleDBService("temp.vdb3"))
            {
                s.CreateDomain("d1");

                tb = DBHelpers.GetDomainDirectory("temp.vdb3", false);
                idDomain = (int)tb.Rows[0]["idDomain"];
                s.DeleteDomain("d1");

                tb = DBHelpers.GetDomainDirectory("temp.vdb3", false);
                Assert.AreEqual(0, tb.Rows.Count); // the domain is not "visible" anymore, because it has been marked as to be deleted
                tb = DBHelpers.GetDomainDirectory("temp.vdb3", true);
                Assert.AreEqual(1, tb.Rows.Count); // so there still is 1 row marked for deletion
            }

            tb = DBHelpers.GetDomainDirectory("temp.vdb3", true);
            Assert.AreEqual(0, tb.Rows.Count); // the domain is gone, because the domain GC has been run during service shutdown

            // and also the domain table is gone now
            try
            {
                DataTable tbD = DBHelpers.GetDomainByIdDomain("temp.vdb3", idDomain);
                Assert.Fail();
            }
            catch (VistaDB.Diagnostic.VistaDBException ex)
            {
                Assert.AreEqual(572, ex.ErrorId);
            }

            // delete non-existent domain
            using (ISimpleDBService s = new NSimpleDB.Service.VistaDb.VistaDbSimpleDBService("temp.vdb3"))
            {
                s.DeleteDomain("xxx");
            }
        }


        [Test]
        public void testListAll()
        {
            System.IO.File.Delete("temp.vdb3");
            using (ISimpleDBService s = new NSimpleDB.Service.VistaDb.VistaDbSimpleDBService("temp.vdb3"))
            {
                string[] domainNameList;
                domainNameList = s.ListDomains();
                Assert.AreEqual(0, domainNameList.Length);

                s.CreateDomain("c");
                domainNameList = s.ListDomains();
                Assert.AreEqual(1, domainNameList.Length);
                Assert.AreEqual("c", domainNameList[0]);

                s.CreateDomain("a");
                domainNameList = s.ListDomains();
                Assert.AreEqual(2, domainNameList.Length);
                Assert.AreEqual("a", domainNameList[0]);
                Assert.AreEqual("c", domainNameList[1]);

                s.DeleteDomain("c");
                domainNameList = s.ListDomains();
                Assert.AreEqual(1, domainNameList.Length);
                Assert.AreEqual("a", domainNameList[0]);
            }
        }

        
        [Test]
        public void testListPaged()
        {
            System.IO.File.Delete("temp.vdb3");
            using (ISimpleDBService s = new NSimpleDB.Service.VistaDb.VistaDbSimpleDBService("temp.vdb3"))
            {
                string[] domainNameList;
                string nextToken = null;

                Assert.AreEqual(0, s.ListDomains(10, ref nextToken).Length);
                Assert.IsNull(nextToken);

                s.CreateDomain("a");
                s.CreateDomain("b");
                s.CreateDomain("c");

                domainNameList = s.ListDomains(3, ref nextToken);
                Assert.AreEqual(3, domainNameList.Length);
                Assert.IsNull(nextToken);

                domainNameList = s.ListDomains(2, ref nextToken);
                Assert.AreEqual(2, domainNameList.Length);
                Assert.IsNotNull(nextToken);
                domainNameList = s.ListDomains(ref nextToken);
                Assert.AreEqual(1, domainNameList.Length);
                Assert.IsNull(nextToken);

                domainNameList = s.ListDomains(1, ref nextToken);
                Assert.AreEqual(1, domainNameList.Length);
                Assert.IsNotNull(nextToken);
                domainNameList = s.ListDomains(1, ref nextToken);
                Assert.AreEqual(1, domainNameList.Length);
                Assert.IsNotNull(nextToken);
                domainNameList = s.ListDomains(1, ref nextToken);
                Assert.AreEqual(1, domainNameList.Length);
                Assert.IsNull(nextToken);

                domainNameList = s.ListDomains(1, ref nextToken);
                Assert.AreEqual(1, domainNameList.Length);
                Assert.IsNotNull(nextToken);
                domainNameList = s.ListDomains(2, ref nextToken);
                Assert.AreEqual(2, domainNameList.Length);
                Assert.IsNull(nextToken);

                domainNameList = s.ListDomains(1, ref nextToken);
                Assert.AreEqual(1, domainNameList.Length);
                Assert.IsNotNull(nextToken);
                domainNameList = s.ListDomains(10, ref nextToken);
                Assert.AreEqual(2, domainNameList.Length);
                Assert.IsNull(nextToken);

                nextToken = "hello";
                Assert.AreEqual(0, s.ListDomains(ref nextToken).Length);
                Assert.IsNull(nextToken);
            }
        }


        [Test]
        public void testListPagedWithGC()
        {
            System.IO.File.Delete("temp.vdb3");
            using (ISimpleDBService s = new NSimpleDB.Service.VistaDb.VistaDbSimpleDBService("temp.vdb3"))
            {
                string[] domainNameList1, domainNameList2;
                string nextToken1 = null;
                string nextToken2 = null;

                s.CreateDomain("a");
                s.CreateDomain("b");
                s.CreateDomain("c");
                domainNameList1 = s.ListDomains(1, ref nextToken1);
                Assert.AreEqual(1, domainNameList1.Length);
                Assert.IsNotNull(nextToken1);

                // wait for first resultset to expire
                System.Threading.Thread.Sleep(4000);

                // create a new resultset - this should trigger the resultset page buffer GC
                domainNameList2 = s.ListDomains(2, ref nextToken2);
                Assert.AreEqual(2, domainNameList2.Length);
                Assert.IsNotNull(nextToken2);

                // check, if unused pages of first resultset have been GCed
                domainNameList1 = s.ListDomains(ref nextToken1);
                Assert.AreEqual(0, domainNameList1.Length);
                Assert.IsNull(nextToken1);
            }
        }

        [Test]
        public void testListWithNullToken()
        {
            System.IO.File.Delete("temp.vdb3");
            using (ISimpleDBService s = new NSimpleDB.Service.VistaDb.VistaDbSimpleDBService("temp.vdb3"))
            {
                string[] domainNameList;
                string nextToken = "";

                s.CreateDomain("a");
                domainNameList = s.ListDomains(1, ref nextToken);
                Assert.AreEqual(1, domainNameList.Length);
                Assert.IsNull(nextToken);

                domainNameList = s.ListDomains(ref nextToken);
                Assert.AreEqual(0, domainNameList.Length);
                Assert.IsNull(nextToken);
            }
        }
    }
}
